package org.jeecg.modules.scene.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhouwr.common.device.vo.InstanceDataStructure;
import com.zhouwr.common.device.vo.function.InstanceFunctionVo;
import com.zhouwr.common.enums.DataRwAuthor;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.device.entity.DeviceInstance;
import org.jeecg.modules.device.entity.DeviceLabel;
import org.jeecg.modules.device.entity.DeviceModel;
import com.zhouwr.common.enums.DeviceInstanceState;
import org.jeecg.modules.device.enums.FunctionExecWay;
import org.jeecg.modules.device.mapper.DataReportMapper;
import org.jeecg.modules.device.service.*;
import org.jeecg.modules.device.vo.DataReportCountVo;
import org.jeecg.modules.device.vo.DeviceInstanceTree;
import org.jeecg.modules.message.service.IMessageEventService;
import org.jeecg.modules.scene.entity.Scene;
import org.jeecg.modules.scene.service.IDisplayService;
import org.jeecg.modules.scene.service.ISceneSchemeService;
import org.jeecg.modules.scene.service.ISceneService;
import org.jeecg.modules.scene.vo.*;
import org.jeecg.utils.TreeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: 场景管理
 * @Author: zhouwr
 * @Date: 2020-06-04
 * @Version: V1.0
 */
@Slf4j
@RestController
@Api(tags = "场景大屏显示")
@RequestMapping("/display")
public class SceneDisplayController {
    @Autowired
    private ISceneService sceneService;
    @Autowired
    private ISceneSchemeService sceneSchemeService;
    @Autowired
    private IDeviceInstanceService instanceService;
    @Autowired
    private IDeviceInstanceParamService instanceParamService;
    @Autowired
    private IDeviceModelService deviceModelService;
    @Autowired
    private IMessageEventService messageEventService;
    @Autowired
    private IDataReportService dataReportService;
    @Autowired
    private ISysBaseAPI sysBaseAPI;
    @Autowired
    private IDisplayService displayService;
    @Autowired
    private IDeviceLabelService labelService;

    @Autowired
    private DataReportMapper reportMapper;


    @AutoLog(value = "场景大屏显示-维持登录状态")
    @ApiOperation(value = "场景大屏显示-维持登录状态", notes = "场景大屏显示-维持登录状态")
    @GetMapping(value = "/keepLogin")
    public Result<?> keepLogin() {
        log.info(">>>>>>>>>>>> 维持登录状态 <<<<<<<<<<<<");
        return Result.ok();
    }

    @ApiOperation(value = "跳转到大屏显示页面")
    @GetMapping("/toDisplay")
    public ModelAndView toDisplay(String token, ModelAndView modelAndView) {
        modelAndView.setViewName("display");
        final String username = JwtUtil.getUsername(token);
        log.info(username);
        modelAndView.addObject("token", token);
        modelAndView.addObject("username", username);
        return modelAndView;
    }

    @ApiOperation(value = "获取设备标签配置", notes = "根据设备模型Id，获取设备标签配置")
    @GetMapping(value = "/getDeviceLabelConfig")
    public Result<?> getDeviceLabelConfig(@RequestParam(name = "modelId", required = false) String modelId,
                                          @RequestParam(name = "instanceId", required = true) String instanceId,
                                          @RequestParam(name = "defaultTrigger", required = false) String defaultTrigger,
                                          HttpServletRequest req) {
        final List<DeviceLabel> labelList = labelService.labelConfig(modelId, instanceId, defaultTrigger);
        return Result.ok(labelList);
    }

    /**
     * 根据实例id查询实例功能
     *
     * @return
     */
    @ApiOperation(value = "获取设备功能配置", notes = "根据实例id查询实例功能")
    @GetMapping(value = "/getDeviceFunction")
    public Result<?> getDeviceFunction(@RequestParam(name = "instanceId", required = true) String instanceId) {
            List<InstanceFunctionVo> functionVos = instanceService.buildFunctionStructures(instanceId)
                    .stream()
                    .filter(functionStructure -> !FunctionExecWay.AUTO.getCode().equals(functionStructure.getType()))
                    .peek(functionVo -> {
                        functionVo.getInputParams().forEach(inputParam -> {
                            if (StringUtils.isNotBlank(instanceId)) {
                                final Object value = instanceParamService.getInstanceParams(instanceId, functionVo.getId(), inputParam.getId()).getValue();
                                if (value != null) {
                                    inputParam.setValue(value.toString());
                                } else {
                                    inputParam.setValue(inputParam.getMetadata().getValue());
                                }
                            }
                        });
                    })
                    .collect(Collectors.toList());
        return Result.ok(functionVos);
    }

    @AutoLog(value = "场景大屏显示-获取当前登录机构下的场景配置信息")
    @ApiOperation(value = "场景大屏显示-获取场景配置信息", notes = "场景大屏显示-获取当前登录机构下的场景配置信息")
    @GetMapping(value = "/getDeployConfig")
    public Result<?> getDeployConfig(@RequestParam(name = "id", required = false) String id,
                                     @RequestParam(name = "username", required = false) String username,
                                     HttpServletRequest req) {
        DeployConfig deployConfig = null;
        Scene scene = sceneService.getCurrentScene(id, username);
        deployConfig = sceneService.buildDeploy(scene);
        return Result.ok(deployConfig);
    }

    @ApiOperation(value = "场景大屏显示-获取设备数据节点", notes = "获取设备数据节点，数据值取最新一条数据值")
    @GetMapping(value = "/getDeviceData")
    public Result<?> getDeviceData(@RequestParam(name = "sceneId", required = false) String sceneId, HttpServletRequest req) {
        List<DeviceInstance> instanceList = instanceService.lambdaQuery().eq(DeviceInstance::getSceneBy, sceneId)
                // TODO 筛选有效方案的设备
                // .in(DeviceInstance::getSceneSchemeBy, new Object())
                .ne(DeviceInstance::getStatus, DeviceInstanceState.NOT_ACTIVE)
                .orderByAsc(DeviceInstance::getName).list();

        List<DeviceDataVO> deviceDataList = instanceList.stream().map(instance -> {
            List<InstanceDataStructure> list = instanceService.listInstanceDataLatest(instance.getId());
            return new DeviceDataVO(instance, list);
        }).collect(Collectors.toList());

        return Result.ok(deviceDataList);
    }

    @AutoLog(value = "场景大屏显示-获取场景方案列表")
    @ApiOperation(value = "场景大屏显示-获取场景方案列表", notes = "场景大屏显示-获取场景方案列表")
    @GetMapping(value = "/getSceneSchemeCount")
    public Result<?> getSceneSchemeCount(@RequestParam(name = "sceneId", required = true) String sceneId, HttpServletRequest req) {
        return Result.ok(sceneSchemeService.schemeCountListBySceneId(sceneId));
    }

    @AutoLog(value = "场景大屏显示-根据id获取设备模型")
    @ApiOperation(value = "场景大屏显示-根据id获取设备模型", notes = "场景大屏显示-根据id获取设备模型")
    @GetMapping(value = "/getModelById")
    public Result<?> getModelById(@RequestParam(name = "id", required = true) String id) {
        return Result.ok(deviceModelService.getById(id));
    }

    @AutoLog(value = "场景大屏显示-所有设备数量统计")
    @ApiOperation(value = "场景大屏显示-所有设备数量统计", notes = "场景大屏显示-所有设备数量统计")
    @GetMapping(value = "/getDeviceCount")
    public Result<?> getDeviceCount(@RequestParam(name = "sceneId", required = true) String sceneId,
                                    @RequestParam(name = "schemeId", required = false) String schemeId) {

        QueryWrapper<DeviceInstance> queryWrapper = new QueryWrapper<DeviceInstance>();
        queryWrapper.eq("scene_by", sceneId);
        if (oConvertUtils.isNotEmpty(schemeId)) {
            queryWrapper.eq("scene_scheme_by", schemeId);
        }
        queryWrapper.ne("status", DeviceInstanceState.NOT_ACTIVE.getCode());
        List<DeviceInstance> instances = instanceService.list(queryWrapper);
        Map<String, List<DeviceInstance>> countMap = instances.parallelStream().collect(Collectors.groupingBy(DeviceInstance::getModelBy));
        List<DeviceCountVo> voList = countMap.keySet().stream().map(modelId -> {
            DeviceModel model = deviceModelService.getById(modelId);
            return new DeviceCountVo(
                    modelId, model.getName(), model.getCode(), countMap.get(modelId).size()
            );
        }).collect(Collectors.toList());

        return Result.ok(voList);
    }

    @ApiOperation(value = "场景大屏显示-设备在线状态", notes = "场景大屏显示-设备在线状态")
    @GetMapping(value = "/getCountDailyDataByMonth")
    public Result<?> getCountDailyDataByMonth() {
        return Result.ok(reportMapper.countDailyDataByMonth());
    }


    @AutoLog(value = "场景大屏显示-设备在线状态")
    @ApiOperation(value = "场景大屏显示-设备在线状态", notes = "场景大屏显示-设备在线状态")
    @GetMapping(value = "/getDeviceState")
    public Result<?> getDeviceState(@RequestParam(name = "sceneId", required = false) String sceneId,
                                    @RequestParam(name = "schemeId", required = false) String schemeId) {

        QueryWrapper<DeviceInstance> queryWrapper = new QueryWrapper<DeviceInstance>();
        if (oConvertUtils.isNotEmpty(sceneId)) {
            queryWrapper.eq("scene_by", sceneId);
        }
        if (oConvertUtils.isNotEmpty(schemeId)) {
            queryWrapper.eq("scene_scheme_by", schemeId);
        }
        queryWrapper.ne("status", DeviceInstanceState.NOT_ACTIVE.getCode());

        List<DeviceInstance> instances = instanceService.list(queryWrapper);

        long online = instances.stream().filter(instance -> instance.getStatus().equals(DeviceInstanceState.ONLINE)).count();
        long offline = instances.stream().filter(instance -> instance.getStatus().equals(DeviceInstanceState.OFFLINE)).count();
        DeviceStateVo state = new DeviceStateVo(online, offline);
        return Result.ok(state);
    }

    @AutoLog(value = "场景大屏显示-获取消息事件统计")
    @GetMapping(value = "/getMessageEventStat")
    public Result<?> getMessageEventStat(@RequestParam(name = "sceneId", required = true) String sceneId) {
        try {
            final List<MessageEventCountVo> countVos = displayService.statMessageEvent(sceneId);
            return Result.ok(countVos);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(e.getMessage());
        }
    }

    @AutoLog(value = "场景大屏显示-获取消息事件统计")
    @GetMapping(value = "/listMessageEvent")
    public Result<?> listMessageEvent(@RequestParam(name = "sceneId", required = true) String sceneId) {
        return Result.ok(null);
    }

    @AutoLog(value = "场景大屏显示-获取最新数据")
    @GetMapping(value = "/listLatestReportData")
    public Result<?> listLatestReportData(@RequestParam(name = "sceneId", required = true) String sceneId) {
        try {
            List<InstanceDataStructure> list = new ArrayList<>();
            for (DeviceInstance instances : instanceService.lambdaQuery().eq(DeviceInstance::getSceneBy, sceneId).list()) {
                list.addAll(
                        instanceService.listInstanceDataLatest(instances.getId())
                                .stream()
                                .filter(instanceDataStructure -> instanceDataStructure.getRwAuthor().equals(DataRwAuthor.READ))
                                .collect(Collectors.toList())
                );
            }
            return Result.ok(list);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(e.getMessage());
        }
    }

    @AutoLog(value = "场景大屏显示-获取历史数据")
    @GetMapping(value = "/listHistoryReportData")
    public Result<?> listHistoryReportData(@RequestParam(name = "instanceId", required = true) String instanceId) {
        try {
            // 24小时
            Date now = new Date();
            Date begin = new Date(now.getTime() - (24 * 60 * 60 * 1000));
            final List<InstanceDataStructure> list = instanceService.listInstanceDataHistory(instanceId, begin, now)
                    .stream()
                    .filter(instanceDataStructure -> instanceDataStructure.getRwAuthor().equals(DataRwAuthor.READ))
                    .collect(Collectors.toList());
            return Result.ok(list);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(e.getMessage());
        }
    }

    @AutoLog(value = "场景大屏显示-获取数据统计信息")
    @GetMapping(value = "/getDataCount")
    public Result<?> getDataCount(@RequestParam(name = "sceneId", required = true) String sceneId) {
        List<DataReportCountVo> list = dataReportService.getReportDataCount(sceneId);
        return Result.ok(list);
    }

    @AutoLog(value = "场景大屏显示-获取设备拓扑关系")
    @GetMapping(value = "/getDeviceTopology")
    public Result<?> getDeviceTopology(@RequestParam(name = "instanceId", required = true) String instanceId) {
        List<DeviceInstance> children = instanceService.getInstanceChildren(instanceId, false);
        List<DeviceInstance> parentInstance = instanceService.getParentInstances(instanceId, new ArrayList<DeviceInstance>());
        children.addAll(parentInstance);
        List<DeviceInstanceTree> instanceTrees = children.stream().map(DeviceInstanceTree::new).collect(Collectors.toList());
        List<DeviceInstanceTree> deviceInstanceTrees = TreeUtil.listToTree(instanceTrees);
        return Result.ok(deviceInstanceTrees);
    }

    @AutoLog(value = "场景大屏显示-获取所有设备拓扑关系")
    @GetMapping(value = "/getAllDeviceTopology")
    public Result<?> getAllDeviceTopology() {
        List<DeviceInstanceTree> deviceInstanceTrees = TreeUtil.listToTree(
                instanceService.list()
                        .stream()
                        .map(DeviceInstanceTree::new)
                        .collect(Collectors.toList())
        );
        return Result.ok(deviceInstanceTrees);
    }
}